#include "Defines.h"
#include "Tools/linked_list.h"
#include "SQLInterface/Tuple.h"
#include "SQLInterface/Database.h"
#include "SQLInterface/SQLInterface.h"
#include "Parsing/Parsing.h"
#include "Tools/Sort.h"

#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

node_t* getStockDataTuples(char* symbol);

//result_type updateStockStatsTable(const char* const symbol, const int length, const double sum, const double sumOfSquares);
//tuple_t* buildStockStatsTuple(const char* const symbol, const int length, const double sum, const double sumOfSquares);

//result_type updateCorrelationDataTable(const char* const symbol1, const char* const symbol2, const CorrelationID id, const double value, const double sumOfProducts, int length);
//tuple_t* buildCorrelationDataTuple(const char* const symbol1, const char* const symbol2, const CorrelationID id, const double value, const double sumOfProducts, int length);

int insertInitialCorrelation(const char* const name, const char* const description, const char* const function);
tuple_t* buildCorrelationTuple(const char* const name, const char* const description, const char* const function);

result_type updateCorrelationDataTable(const char* const symbol1, const char* const symbol2, 
                                        const int id, const double value, 
                                        const double sumOfProducts, int length,
                                        const double stock1Sum, const double stock2Sum,
                                        const double stock1SumSquares, const double stock2SumSquares);

tuple_t* buildCorrelationDataTuple(const char* const symbol1, const char* const symbol2, 
                                   const int id, const double value, 
                                   const double sumOfProducts, int length,
                                   const double stock1Sum, const double stock2Sum,
                                   const double stock1SumSquares, const double stock2SumSquares);

int main()
{
    // sum(x) = summation of all values in x
    // n = number of values in each set
    // = (n * sum(x * y)) - sum(x)sum(y)) /
    //   (sqrt((n * sum(x^2)) - sum(x)^2) * sqrt((n * sum(y^2)) - sum(y)^2))

    double sumX = 0.0;
    double sumY = 0.0;
    double sumXSquared = 0.0;
    double sumYSquared = 0.0;
    double sumProducts = 0.0;
    double adjustedPrice1 = 0.0;
    double adjustedPrice2 = 0.0;
    double correlation = 0.0;
    int n = 0;
    node_t* list1;
    node_t* list2;
    node_t* iterator1;
    node_t* iterator2;
    tuple_t* tuple1;
    tuple_t* tuple2;
    result_type result;
    int i, i2, i3;
    char** symbolList = NULL;
    int symbolListLength = 0;
    int numTuples1 = 0;
    int numTuples2 = 0;
    int correlationId;
    int end = 0;

    // TODO: add symbol list download/update

    // parse symbol list
    result = parse_symbols(&symbolList, &symbolListLength); //assumes eoddata format...

    if(result != SUCCESS)
    {
        return 1;
    }

    // initial correlation
    correlationId = insertInitialCorrelation("Indefinite", 
        "Correlation over the lifetime of the newer stock.", 
        //" ((n * sum(x * y)) - (sum(x) * sum(y))) / (sqrt((n * sum(x^2)) - sum(x)^2) * sqrt((n * sum(y^2)) - sum(y)^2))");
        "Pearsons");

    if(correlationId < 0)
    {
        printf("Error creating correlation in correlation table\n");
        return 1;
    }

    // note that this will always put symbol 1 as higher in alphabetical order
    // than symbol 2...
    for(i = 0; i < symbolListLength; i++)
    {
        printf("Fetching data for %s...\n", symbolList[i]);
        list1 = getStockDataTuples(symbolList[i]);

        if(list1 == NULL)
        {
            printf("No Data Found.\n");
            continue;
        }

        // sort list and get length
        printf("Sorting data for %s...\n", symbolList[i]);
        numTuples1 = sortStockDataByDate(&list1);

        for(i2 = i+1; i2 < symbolListLength; i2++)
        {

            printf("Fetching data for %s...\n", symbolList[i2]);
            list2 = getStockDataTuples(symbolList[i2]);

            if(list2 == NULL)
            {
                printf("No Data Found.\n");
                continue;
            }

            // TODO: store this sort somewhere
            printf("Sorting data for %s...\n", symbolList[i2]);
            numTuples2 = sortStockDataByDate(&list2);

            printf("Calculating correlation between %s and %s...\n", symbolList[i], symbolList[i2]);

            if(numTuples2 > numTuples1)
            {
                n = numTuples1;
            }
            else
            {
                n = numTuples2;
            }

            // calculate sums
            // TODO: do string conversions if needed...
            iterator1 = list1;
            iterator2 = list2;
            sumX = 0;
            sumY = 0;
            sumXSquared = 0;
            sumYSquared = 0;
            sumProducts = 0;
            end = 0;

            for(i3 = 0; i3 < n; i3++)
            {

                tuple1 = (tuple_t*)(iterator1->element);
                tuple2 = (tuple_t*)(iterator2->element);

                while(strcmp((char*)(tuple1->params[STOCK_DATA_PARAM_DATE]),
                      (char*)(tuple2->params[STOCK_DATA_PARAM_DATE])) != 0
                      && n > 0)
                {
                    // TODO: better error correction here, we don't correct n gracefully...
                    n--;

                    printf("mismatch ");
                    printf("%s", (char*)(tuple1->params[STOCK_DATA_PARAM_DATE]));
                    printf(" ");
                    printf("%s", (char*)(tuple2->params[STOCK_DATA_PARAM_DATE]));
                    printf("\n");
                    if(dateToInt((char*)(tuple1->params[STOCK_DATA_PARAM_DATE])) > 
                        dateToInt((char*)(tuple2->params[STOCK_DATA_PARAM_DATE])))
                    {
                        iterator1 = iterator1->next;
                        if(iterator1 != NULL)
                        {
                            tuple1 = (tuple_t*)(iterator1->element);
                        }
                        else
                        {
                            end = 1;
                            break;
                        }
                    }
                    else
                    {
                        iterator2 = iterator2->next;
                        if(iterator1 != NULL)
                        {
                            tuple2 = (tuple_t*)(iterator2->element);
                        }
                        else
                        {
                            end = 1;
                            break;
                        }
                    }
                }

                if(end == 1 || n <= 0)
                {
                    break;
                }

                adjustedPrice1 = atof((char*)(tuple1->params[STOCK_DATA_PARAM_ADJ_CLOSE]));
                adjustedPrice2 = atof((char*)(tuple2->params[STOCK_DATA_PARAM_ADJ_CLOSE]));

                sumX += adjustedPrice1;
                sumY += adjustedPrice2;
                sumXSquared += pow(adjustedPrice1, 2);
                sumYSquared += pow(adjustedPrice2, 2);
                sumProducts += adjustedPrice1 * adjustedPrice2;

                iterator1 = iterator1->next;
                iterator2 = iterator2->next;
            }

            if(n <= 0)
            {
                printf("No valid comparisons...\n");
                freeTupleList(list2);
                continue;
            }

             // the actual calculation magic!
            correlation = ((((double)n) * sumProducts) - (sumX * sumY));

            // 
            //if(correlation < 0.001 && correlation > -0.001)
            //{
                //printf("in if 0.001\n");
                //correlation = 0.0;
            //}
            //else
            //{
                correlation /= (sqrt((n * sumXSquared) - pow(sumX, 2)) * sqrt((n * sumYSquared) - pow(sumY, 2)));
            //}


            printf("Updating Database...\n");
            if(updateCorrelationDataTable(symbolList[i], symbolList[i2], 
                                          correlationId, correlation, sumProducts,
                                          n, sumX, sumY, sumXSquared, sumYSquared)
                                          == SUCCESS)
            {
                printf("Success!!!\n\n");
            }
            else
            {
                printf("Error Updating Database...\n\n");
            }

            freeTupleList(list2);
        }// end inner symbol loop

        freeTupleList(list1);
    }// end outer symbol loop

    return 0;
}


node_t* getStockDataTuples(char* symbol)
{
    tuple_t* selectTuple = initTuple(TABLE_STOCK_DATA);
    selectTuple->params[STOCK_DATA_PARAM_SYMBOL] = (char*)malloc(strlen(symbol) + 1);
    strcpy((char*)(selectTuple->params[STOCK_DATA_PARAM_SYMBOL]), symbol);
    return SQL_select(selectTuple);
}
/*
result_type updateStockStatsTable(const char* const symbol, const int length, const double sum, const double sumOfSquares)
{
    tuple_t* tuple = buildStockStatsTuple(symbol, length, sum, sumOfSquares);

    return SQL_insert(tuple);
}*/

int insertInitialCorrelation(const char* const name, const char* const description, const char* const function)
{
    tuple_t* tuple = buildCorrelationTuple(name, description, function);
    node_t* selectList;

    if(SQL_insert(tuple) == SUCCESS)
    {
        tuple = buildCorrelationTuple(name, description, function);
        free(tuple->params[CORRELATION_PARAM_ID]);
        tuple->params[CORRELATION_PARAM_ID] = NULL;

        selectList = SQL_select(tuple);
        if(selectList && selectList->element)
        {
            return atoi((char*)(((tuple_t*)(selectList->element))->params[CORRELATION_PARAM_ID]));
        }
        else
        {
            return -1;
        }
    }

    return -1;
}


result_type updateCorrelationDataTable(const char* const symbol1, const char* const symbol2, 
                                        const int id, const double value, 
                                        const double sumOfProducts, int length,
                                        const double stock1Sum, const double stock2Sum,
                                        const double stock1SumSquares, const double stock2SumSquares)
{
    tuple_t* tuple = buildCorrelationDataTuple(symbol1, symbol2, id, value, 
                                               sumOfProducts, length,
                                               stock1Sum, stock2Sum,
                                               stock1SumSquares, stock2SumSquares);

    return SQL_insert(tuple);
}

/*
tuple_t* buildStockStatsTuple(const char* const symbol, const int length, const double sum, const double sumOfSquares)
{
    tuple_t* tuple = initTuple(TABLE_STOCK_STATS);
    int i;

    // just for ease...should probably fix this later
    for(i = 0; i < getNumParams(TABLE_STOCK_STATS); i++)
    {
        tuple->params[i] = malloc(sizeof(char) * 50);
    }

    strcpy(tuple->params[STOCK_STATS_PARAM_SYMBOL], symbol);
    sprintf(tuple->params[STOCK_STATS_PARAM_CORRELATION_LENGTH], "%d", length);
    sprintf(tuple->params[STOCK_STATS_PARAM_SUM], "%f", sum);
    sprintf(tuple->params[STOCK_STATS_PARAM_SUM_OF_SQUARES], "%f", sumOfSquares);

    return tuple;
}*/

tuple_t* buildCorrelationDataTuple(const char* const symbol1, const char* const symbol2, 
                                   const int id, const double value, 
                                   const double sumOfProducts, int length,
                                   const double stock1Sum, const double stock2Sum,
                                   const double stock1SumSquares, const double stock2SumSquares)
{
    tuple_t* tuple = initTuple(TABLE_CORRELATION_DATA);
    int i;

    // just for ease...should probably fix this later
    for(i = 0; i < getNumParams(TABLE_CORRELATION_DATA); i++)
    {
        tuple->params[i] = malloc(sizeof(char) * 50);
    }

    strcpy(tuple->params[CORRELATION_DATA_PARAM_SYMBOL_1], symbol1);
    strcpy(tuple->params[CORRELATION_DATA_PARAM_SYMBOL_2], symbol2);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_CORRELATION_ID], "%d", id);
    //if(value < 0.001 && value > -0.001)
    //{
    //    sprintf(tuple->params[CORRELATION_DATA_PARAM_VALUE], "0.0");
    //}
    //else
    //{
        sprintf(tuple->params[CORRELATION_DATA_PARAM_VALUE], "%f", value);
    //}
    sprintf(tuple->params[CORRELATION_DATA_PARAM_SUM_OF_PRODUCTS], "%f", sumOfProducts);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_LENGTH], "%d", length);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_STOCK_1_SUM], "%f", stock1Sum);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_STOCK_2_SUM], "%f", stock2Sum);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_STOCK_1_SUM_OF_SQUARES], "%f", stock1SumSquares);
    sprintf(tuple->params[CORRELATION_DATA_PARAM_STOCK_2_SUM_OF_SQUARES], "%f", stock2SumSquares);
    
    return tuple;
}

tuple_t* buildCorrelationTuple(const char* const name, const char* const description, const char* const function)
{
    tuple_t* tuple = initTuple(TABLE_CORRELATION);
    int i;

    // just for ease...should probably fix this later
    for(i = 0; i < getNumParams(TABLE_CORRELATION); i++)
    {
        tuple->params[i] = malloc(sizeof(char) * 200);
    }

    strcpy(tuple->params[CORRELATION_PARAM_ID],"0");//just takes up space
    strcpy(tuple->params[CORRELATION_PARAM_NAME], name);
    strcpy(tuple->params[CORRELATION_PARAM_DESCRIPTION], description);
    strcpy(tuple->params[CORRELATION_PARAM_FUNCTION], function);

    return tuple;
}
